package libnet

import (
	"fmt"
	"fury/gateway/syncs"
	"net"
	"sync/atomic"
	"time"

	log "github.com/cihub/seelog"
)

var (
	server *Server
)

type Server struct {
	//server static info
	name     string
	addr     string
	listener net.Listener

	// About sessions
	maxSessionId uint64
	sessions     map[uint64]*Session
	sessionMutex syncs.Mutex

	// About server start and stop
	stopFlag int32
	stopWait syncs.WaitGroup

	SendChanSize   int         // Session send chan buffer size.
	ReadBufferSize int         // Session read buffer size.
	State          interface{} // server state.
}

func Listen(protocal string, name string, addr string) (*Server, error) {
	log.Info("start ==> " + name + " at " + addr)
	listener, err := net.Listen(protocal, addr)
	if err != nil {
		return nil, err
	}
	return NewServer(name, addr, listener), nil
}

func NewServer(name string, addr string, listener net.Listener) *Server {
	server = &Server{
		name:     name,
		addr:     addr,
		listener: listener,
		sessions: make(map[uint64]*Session),
		//		SendChanSize:   DefaultSendChanSize,
		//		ReadBufferSize: DefaultConnBufferSize,
	}
	return server
}

func (server *Server) Accept() (*Session, error) {
	fmt.Printf("p=%d\n", *server)
	conn, err := server.listener.Accept()
	if err != nil {
		return nil, err
	}
	return createNewSession(atomic.AddUint64(&server.maxSessionId, 1), conn), nil
}

func createNewSession(id uint64, conn net.Conn) *Session {
	session := NewSession(id, conn, time.Microsecond)
	server.bindSession(session)
	return session
}

func (server *Server) bindSession(session *Session) {
	server.sessionMutex.Lock()
	defer server.sessionMutex.Unlock()

	server.sessions[session.id] = session
	server.stopWait.Add(1)
}

func (server *Server) Serve(handle func(*Session)) error {
	for {
		session, err := server.Accept()
		if err != nil {
			return err
		}
		go handle(session)
	}
}

func (server *Server) stop() {
	if atomic.CompareAndSwapInt32(&server.stopFlag, 0, 1) {
		server.listener.Close()
		server.closeSessions()
		server.stopWait.Wait()
	}
}

// Delete a session from session list.
func (server *Server) delSession(session *Session) {
	server.sessionMutex.Lock()
	defer server.sessionMutex.Unlock()

	session.RemoveCloseCallback(server)
	delete(server.sessions, session.id)
	server.stopWait.Done()
}

// Copy sessions for close.
func (server *Server) copySessions() []*Session {
	server.sessionMutex.Lock()
	defer server.sessionMutex.Unlock()

	sessions := make([]*Session, 0, len(server.sessions))
	for _, session := range server.sessions {
		sessions = append(sessions, session)
	}
	return sessions
}

// Fetch sessions.
func (server *Server) fetchSession(callback func(*Session)) {
	server.sessionMutex.Lock()
	defer server.sessionMutex.Unlock()

	for _, session := range server.sessions {
		callback(session)
	}
}

// Close all sessions.
func (server *Server) closeSessions() {
	// copy session to avoid deadlock
	sessions := server.copySessions()
	for _, session := range sessions {
		session.Close()
	}
}
